package com.kaoshi.myweb.common.utils.security;

import java.io.ByteArrayOutputStream;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.InputStream;
import java.io.ObjectInputStream;
import java.math.BigInteger;
import java.net.URL;
import java.security.CodeSource;
import java.security.KeyFactory;
import java.security.KeyPair;
import java.security.KeyPairGenerator;
import java.security.NoSuchAlgorithmException;
import java.security.PrivateKey;
import java.security.ProtectionDomain;
import java.security.PublicKey;
import java.security.SecureRandom;
import java.security.interfaces.RSAPrivateKey;
import java.security.interfaces.RSAPublicKey;
import java.security.spec.InvalidKeySpecException;
import java.security.spec.RSAPrivateKeySpec;
import java.security.spec.RSAPublicKeySpec;
import java.util.Properties;

import javax.crypto.Cipher;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;



/**
 * RSA 工具类。提供加密，解密，生成密钥对等方法。
 * 需要到http://www.bouncycastle.org下载bcprov-jdk14-123.jar。
 */
public class RSAUtil
{
	private static Logger logger = LoggerFactory.getLogger(RSAUtil.class.getName());

	private static Properties  props = new Properties();
    private static String CONFIG_FILE_NAME = "/key.properties";
	/**
     * * 生成密钥对 *
     *
     * @return KeyPair *
     */
    public static KeyPair generateKeyPair() throws Exception
    {
        try
        {
            KeyPairGenerator keyPairGen = KeyPairGenerator.getInstance("RSA", new org.bouncycastle.jce.provider.BouncyCastleProvider());
            final int KEY_SIZE = 1024;// 没什么好说的了，这个值关系到块加密的大小，可以更改，但是不要太大，否则效率会低
            keyPairGen.initialize(KEY_SIZE, new SecureRandom());
            KeyPair keyPair = keyPairGen.generateKeyPair();
            saveKeyPair(keyPair);
            return keyPair;
        }
        catch (Exception e)
        {
            throw new Exception(e.getMessage());
        }
    }

    public static KeyPair getKeyPair() throws Exception
    {
        FileInputStream fis = new FileInputStream("RSAKey.txt");
        ObjectInputStream oos = new ObjectInputStream(fis);
        KeyPair kp = (KeyPair) oos.readObject();
        oos.close();
        fis.close();
        return kp;
    }

    public static void saveKeyPair(KeyPair kp) throws Exception
    {
    	RSAPublicKey publicKey = (RSAPublicKey)kp.getPublic();
    	RSAPrivateKey privateKey = (RSAPrivateKey)kp.getPrivate();
    	String n = publicKey.getModulus().toString(16);
    	String e = publicKey.getPublicExponent().toString(16);
    	String d = privateKey.getPrivateExponent().toString(16);
    	//写入classes/tssg.properties文件中保存
    	setProperties("n",n);
    	setProperties("e",e);
    	setProperties("d",d);
    }

    /**
     * * 生成公钥 *
     *
     * @param modulus        *
     * @param publicExponent *
     * @return RSAPublicKey *
     * @throws Exception
     */
    public static RSAPublicKey generateRSAPublicKey(byte[] modulus, byte[] publicExponent) throws Exception
    {
        KeyFactory keyFac = null;
        try
        {
            keyFac = KeyFactory.getInstance("RSA", new org.bouncycastle.jce.provider.BouncyCastleProvider());
        }
        catch (NoSuchAlgorithmException ex)
        {
            throw new Exception(ex.getMessage());
        }
        RSAPublicKeySpec pubKeySpec = new RSAPublicKeySpec(new BigInteger(
                modulus), new BigInteger(publicExponent));
        try
        {
            return (RSAPublicKey) keyFac.generatePublic(pubKeySpec);
        }
        catch (InvalidKeySpecException ex)
        {
            throw new Exception(ex.getMessage());
        }
    }

    /**
     * * 生成私钥 *
     *
     * @param modulus         *
     * @param privateExponent *
     * @return RSAPrivateKey *
     * @throws Exception
     */
    public static RSAPrivateKey generateRSAPrivateKey(byte[] modulus, byte[] privateExponent) throws Exception
    {
        KeyFactory keyFac = null;
        try
        {
            keyFac = KeyFactory.getInstance("RSA", new org.bouncycastle.jce.provider.BouncyCastleProvider());
        }
        catch (NoSuchAlgorithmException ex)
        {
            throw new Exception(ex.getMessage());
        }
        RSAPrivateKeySpec priKeySpec = new RSAPrivateKeySpec(new BigInteger(modulus), new BigInteger(privateExponent));
        try
        {
            return (RSAPrivateKey) keyFac.generatePrivate(priKeySpec);
        }
        catch (InvalidKeySpecException ex)
        {
            throw new Exception(ex.getMessage());
        }
    }

    /**
     * * 加密 *
     *
     * @param key  加密的密钥 *
     * @param data 待加密的明文数据 *
     * @return 加密后的数据 *
     * @throws Exception
     */
    public static byte[] encrypt(PublicKey pk, byte[] data) throws Exception
    {
        try
        {
        	Cipher cipher = Cipher.getInstance("RSA",
                    new org.bouncycastle.jce.provider.BouncyCastleProvider());
            cipher.init(Cipher.ENCRYPT_MODE, pk);
            int blockSize = cipher.getBlockSize();// 获得加密块大小，如：加密前数据为128个byte，而key_size=1024
            // 加密块大小为127
            // byte,加密后为128个byte;因此共有2个加密块，第一个127
            // byte第二个为1个byte
            int outputSize = cipher.getOutputSize(data.length);// 获得加密块加密后块大小
            int leavedSize = data.length % blockSize;
            int blocksSize = leavedSize != 0 ? data.length / blockSize + 1
                    : data.length / blockSize;
            byte[] raw = new byte[outputSize * blocksSize];
            int i = 0;
            while (data.length - i * blockSize > 0)
            {
                if (data.length - i * blockSize > blockSize)
                {
                    cipher.doFinal(data, i * blockSize, blockSize, raw, i
                            * outputSize);
                }
                else
                {
                    cipher.doFinal(data, i * blockSize, data.length - i
                            * blockSize, raw, i * outputSize);
                }
                // 这里面doUpdate方法不可用，查看源代码后发现每次doUpdate后并没有什么实际动作除了把byte[]放到
                // ByteArrayOutputStream中，而最后doFinal的时候才将所有的byte[]进行加密，可是到了此时加密块大小很可能已经超出了
                // OutputSize所以只好用dofinal方法。
                i++;
            }
            return raw;
        }
        catch (Exception e)
        {
            throw new Exception(e.getMessage());
        }
    }

    /**
     * * 加密 *
     *
     * @param key  加密的密钥 *
     * @param data 待加密的明文数据 *
     * @return 加密后的数据 *
     * @throws Exception
     */
    public static String encrypt(PublicKey pk, String dataStr) throws Exception
    {
        try
        {
        	byte[] data = new BigInteger(dataStr, 16).toByteArray();
        	Cipher cipher = Cipher.getInstance("RSA",
                    new org.bouncycastle.jce.provider.BouncyCastleProvider());
            cipher.init(Cipher.ENCRYPT_MODE, pk);
            int blockSize = cipher.getBlockSize();// 获得加密块大小，如：加密前数据为128个byte，而key_size=1024
            // 加密块大小为127
            // byte,加密后为128个byte;因此共有2个加密块，第一个127
            // byte第二个为1个byte
            int outputSize = cipher.getOutputSize(data.length);// 获得加密块加密后块大小
            int leavedSize = data.length % blockSize;
            int blocksSize = leavedSize != 0 ? data.length / blockSize + 1
                    : data.length / blockSize;
            byte[] raw = new byte[outputSize * blocksSize];
            int i = 0;
            while (data.length - i * blockSize > 0)
            {
                if (data.length - i * blockSize > blockSize)
                {
                    cipher.doFinal(data, i * blockSize, blockSize, raw, i
                            * outputSize);
                }
                else
                {
                    cipher.doFinal(data, i * blockSize, data.length - i
                            * blockSize, raw, i * outputSize);
                }
                // 这里面doUpdate方法不可用，查看源代码后发现每次doUpdate后并没有什么实际动作除了把byte[]放到
                // ByteArrayOutputStream中，而最后doFinal的时候才将所有的byte[]进行加密，可是到了此时加密块大小很可能已经超出了
                // OutputSize所以只好用dofinal方法。
                i++;
            }

            return new String(raw,"UTF-8");
        }
        catch (Exception e)
        {
            throw new Exception(e.getMessage());
        }
    }

    /**
     * * 解密 *
     *
     * @param key 解密的密钥 *
     * @param raw 已经加密的数据 *
     * @return 解密后的明文 *
     * @throws Exception
     */
    public static byte[] decrypt(PrivateKey pk, byte[] raw) throws Exception
    {
        try
        {
            raw=unSignByteAry(raw);
        	Cipher cipher = Cipher.getInstance("RSA", new org.bouncycastle.jce.provider.BouncyCastleProvider());
            cipher.init(cipher.DECRYPT_MODE, pk);
            int blockSize = cipher.getBlockSize();
            ByteArrayOutputStream bout = new ByteArrayOutputStream(64);
            int j = 0;
            while (raw.length - j * blockSize > 0)
            {
                bout.write(cipher.doFinal(raw, j * blockSize, blockSize));
                j++;
            }
            return bout.toByteArray();
        }
        catch (Exception e)
        {
            throw new Exception(e.getMessage());
        }
    }

    /**
     * * 解密 *
     *
     * @param key 解密的密钥 *
     * @param raw 已经加密的数据 *
     * @return 解密后的明文 *
     * @throws Exception
     */
    public static String decrypt(String raw) throws Exception
    {
        try
        {
        	byte[] byRaw = unSignByteAry(new BigInteger(raw, 16).toByteArray());

        	String modulusStr=RSAUtil.getProperties("n");
        	String priExponentStr=RSAUtil.getProperties("d");
        	byte[] modulusArray = new BigInteger(modulusStr, 16).toByteArray();
        	byte[] priExponentArray = new BigInteger(priExponentStr, 16).toByteArray();

        	RSAPrivateKey privateKey = RSAUtil.generateRSAPrivateKey(modulusArray, priExponentArray);
        	Cipher cipher = Cipher.getInstance("RSA", new org.bouncycastle.jce.provider.BouncyCastleProvider());
            cipher.init(cipher.DECRYPT_MODE, privateKey);
            int blockSize = cipher.getBlockSize();
            ByteArrayOutputStream bout = new ByteArrayOutputStream(64);
            int j = 0;
            while (byRaw.length - j * blockSize > 0)
            {
                bout.write(cipher.doFinal(byRaw, j * blockSize, blockSize));
                j++;
            }

            byte[] byresult = bout.toByteArray();
            StringBuffer str = new StringBuffer(new String(byresult));
            if(str!=null)
            	return str.reverse().toString();
            else
            	return "";
        }
        catch (Exception e)
        {
            throw new Exception(e.getMessage());
        }
    }

    /**
     *描述: 过滤0x00标志位
     *@param byteAry
     *@return
     */
    private static byte[] unSignByteAry(byte[] byteAry)
    {

    	if (byteAry[0] != 0x00) {
    		return byteAry;
    	}
    	if (byteAry.length == 1) {
    		return byteAry;
    	}
    	byte[] unSignByteAry = new byte[byteAry.length - 1];
    	for (int i = 0; i < unSignByteAry.length; i++) {
    		unSignByteAry[i] = byteAry[i + 1];
    	}
    	return unSignByteAry;
    }


    private static void setProperties(String propsName,String value)
    {

    	try
		{
			props.setProperty(propsName, value);
			FileOutputStream fileOut = new FileOutputStream(getProperitesPath());
			props.store(fileOut, null);
			if (fileOut != null)
			{
				fileOut.close();
				fileOut = null;
			}
		}
		catch (Exception ex)
		{
			ex.printStackTrace();
			logger.error("出现异常：" + ex.getCause());
		}

    }

	public static String getProperties(String propsName)
	{
		String value = "";
		InputStream in = null;
		try
		{
			in = RSAUtil.class.getResourceAsStream(CONFIG_FILE_NAME);
			props.load(in);
			value = props.getProperty(propsName);
			if (in != null)
			{
				in.close();
				in = null;
			}
		}
		catch (Exception ex)
		{
			ex.printStackTrace();
			logger.error("可能是配置文件未发现！");
		}
		return value;
	}

	public static String getProperitesPath()
	{
		/*
		String classPath = System.getProperty("java.class.path");
		System.out.println("classPath="+classPath);
		String pathSp = System.getProperty("path.separator");
		String fileSp = System.getProperty("file.separator");
		String[] classPathAry = classPath.split(pathSp);
		*/
		String classPath="";
		try
        {
			  final ProtectionDomain pd = RSAUtil.class.getProtectionDomain();


			  if (pd != null)
			  {
				  URL result = null;
				  final CodeSource cs = pd.getCodeSource();
				   if (cs != null)
					    result = cs.getLocation();
				   if (result != null)
				   {
					   classPath=result.getFile();
				   }
			  }
        }
        catch (Exception e)
        {
	        // TODO Auto-generated catch block
	        e.printStackTrace();
        }
        int pos= classPath.indexOf("WEB-INF");
        String fixPath = classPath.substring(0, pos+7);
		String path =fixPath+"/classes"+CONFIG_FILE_NAME;
		return path;
	}




    /**
     * * *
     *
     * @param args *
     * @throws Exception
     */
    public static void main(String[] args) throws Exception
    {
    	/*
        KeyPair keyPair = RSAUtil.generateKeyPair();
        RSAPublicKey rsaPubKey = (RSAPublicKey) keyPair.getPublic();
        RSAPrivateKey rsaPriKey = (RSAPrivateKey) keyPair.getPrivate();

        System.out.println("modules:" + rsaPubKey.getModulus().toString(16));
        System.out.println("exponent:" + rsaPubKey.getPublicExponent().toString(16));
        System.out.println("modules:" + rsaPriKey.getModulus().toString(16));
        System.out.println("exponent:" + rsaPriKey.getPrivateExponent().toString(16));

        long startTime = System.currentTimeMillis();
        String test = "hello world";
        byte[] en_test = encrypt(getKeyPair().getPublic(), test.getBytes());
        byte[] de_test = decrypt(getKeyPair().getPrivate(), en_test);
        System.out.println(new String(de_test));
        System.out.println(System.currentTimeMillis() - startTime);
        */

        /*****************************************************************************************************/

        //与Flash中加解密进行对比:

        //对于Flash中加密的数据，在JAVA中解密

        String modulusStr = "c464c472dbcc46df4f411cc9b34c2fb6b39519668433b02cb934487738536e2b3b59a94163c3e903c91aa72e80a900fb9847c05fd5ab7dc1cfd0d6bb115151f45d2233827e58d4fec176c532d70f08e41d8f897c2b8cd4b17926675e5b19900a74050a69785f9f76aadcd30d15a14603463709cfcb76aa1f999bf092ed64a22f";
        String priExponentStr = "44b600a932cf5088768110625d31ad8bdd5eafe9571d8268d34f407f3d9caaf1c32d067393450ab4415aeadcf134afdc5695dbef6ba1393962cced23bbd658b669854c4ec97e930aee4417d61531f8de62a3fd5a33c97a655dd9a7715aa49b0f76273798b0271e1b71cf0de5c74f17c09f28839b1ecdc6accb17967e7f86bb89";
        String clipherText = "13f6fec2fd534a98c3ea0fef28faa667401c3cfcb15036554ae7ce13876e56cfbffcd20aaa4373463db8e5a6da13ec73ca2b5366ee47592f676ab60e7e3fe3184ac58724800e8f196e4048021c0b34000d3147d4aa68367f57e5644677d07a9f786c3e6cc2cd6248ca74ad470a29648d17dd4819336c2f71fcf68c269f9238f9";

        byte[] modulusArray = new BigInteger(modulusStr, 16).toByteArray();
        byte[] priExponentArray = new BigInteger(priExponentStr, 16).toByteArray();

        RSAPrivateKey privateKey = RSAUtil.generateRSAPrivateKey(modulusArray, priExponentArray);

        byte[] result = RSAUtil.decrypt(privateKey, new BigInteger(clipherText, 16).toByteArray());

        System.out.println(new String(result));

        //sssSystem.out.println(new String(result,result.length-10,10));

        //http://crypto.hurlant.com/demo/
        /*****************************************************************************************************/
    	//RSAUtil.generateKeyPair();
    	//getProperitesPath();
        /*****************************************************************************************************/
    	//System.out.println(RSAUtil.getKeyPair().getPrivate());
    	//System.out.println(RSAUtil.getKeyPair().getPublic());
        //与JavaScript中加解密进行对比:

        //对于JavaScript中加密的数据，在JAVA中解密
        String modulusStr2 =     "130ebebd67b16a9ab2c53a437badbf8f01a80c750095a7fcfe95742c3d5ed1abb318babc5cb5d9350fee4da65ee074f65e1758117e6945f0fcfc8137528053ce9d1da8618890dee24e5e0bf8c87795bb1d09eddd544640824ee0dd0ea9fd908d27b0f8a1ae5c37f3647fbf2f5795500ad76c195b3387d0458a8f51b701472301";
        String priExponentStr2 = "12e8da920d4599458e84ec5ef1656161807f427d05eb79182b7418259d6f6c14364d1f5caf9130c8d9d9d6ea71d1bdbc87781a46a16bcb9e672814fed3b9c96ddffe0a1b0955ae68055c8f92fef518a04fc32a2ea8390e617cc5556a251f9ae9eee70a32e579cb3e9f298848a9b3aaf634f5930ffbf74473f7cb6c0cefee1751";
        String clipherText2 =    "0f78d77cfb286fca88d3c7f74acaca7f9d80ba90754be47743d641f72f73f3f1cb8988625c35dde87b4a3e065f1356a072dbad9cac7f7a3d85ae0bb96922529b95d410d88a1711e30d6ec2037e98114eb9a5423eb4f81ab9da76d3c3548d94341871ff9040e533ae95e16f0eac44e9b1e08fe34fd24400b6f1824c5e5b879722";

        byte[] modulusArray2 = new BigInteger(modulusStr2, 16).toByteArray();
        byte[] priExponentArray2 = new BigInteger(priExponentStr2, 16).toByteArray();

        RSAPrivateKey privateKey2 = RSAUtil.generateRSAPrivateKey(modulusArray2, priExponentArray2);


        byte[] result2 = RSAUtil.decrypt(privateKey2, new BigInteger(clipherText2, 16).toByteArray());
        StringBuffer str = new StringBuffer(new String(result2));
        System.out.println(str.reverse());

        /*
        for (int i = 0; i < result2.length / 2; i++)
        {
              byte temp = result2[i];
              result2[i] = result2[result2.length-i-1];
              result2[result2.length-i-1] = temp;
        }
        System.out.println(new String(result2));
        */

        //http://crypto.hurlant.com/demo/
        /*****************************************************************************************************/
    }
}
